Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | [02977e0004] Reduce impact of recursion depth bug. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | core-8-6-branch |
Files: | files | file ages | folders |
SHA3-256: |
b56973969223f1e2c57319ef270ade92 |
User & Date: | dkf 2024-11-01 15:51:41.055 |
References
2024-11-12
| ||
13:03 | backout [b569739692]: [02977e0004] Reduce impact of recursion depth bug. check-in: a72e9b170e user: jan.nijtmans tags: core-8-6-branch | |
12:10 | backout [b569739692]: [02977e0004] Reduce impact of recursion depth bug. Remove some unneeded typeca... check-in: e1e7f93e74 user: jan.nijtmans tags: core-8-branch | |
09:41 | backout [b569739692]: [02977e0004] Reduce impact of recursion depth bug. check-in: 53462a5b29 user: jan.nijtmans tags: trunk, main | |
2024-11-10
| ||
22:48 | backout [b569739692]: [02977e0004] Reduce impact of recursion depth bug. Closed-Leaf check-in: 1008c8506a user: jan.nijtmans tags: bug-02977e0004 | |
Context
2024-11-10
| ||
22:48 | backout [b569739692]: [02977e0004] Reduce impact of recursion depth bug. Closed-Leaf check-in: 1008c8506a user: jan.nijtmans tags: bug-02977e0004 | |
2024-11-01
| ||
20:56 | fix logic error of test oo-1.24 (deep nested ownership for class) - class destroy doesn't invoke des... check-in: e8755dc826 user: sebres tags: core-8-6-branch | |
19:32 | another variant to fix [02977e0004] (inclusive class deletion) using NRE-callout, without to extend ... check-in: 8d462098e4 user: sebres tags: bug-02977e0004-sebres | |
15:56 | merge 8.6 check-in: a56b3c5c17 user: dkf tags: core-8-branch | |
15:51 | [02977e0004] Reduce impact of recursion depth bug. check-in: b569739692 user: dkf tags: core-8-6-branch | |
12:01 | There still is a buggy case to worry about Closed-Leaf check-in: 9378c3e912 user: dkf tags: bug-02977e0004 | |
2024-10-31
| ||
13:53 | merge 8.5 check-in: b58ad20346 user: sebres tags: core-8-6-branch | |
Changes
Changes to generic/tclNamesp.c.
︙ | ︙ | |||
884 885 886 887 888 889 890 | */ void Tcl_DeleteNamespace( Tcl_Namespace *namespacePtr)/* Points to the namespace to delete. */ { Namespace *nsPtr = (Namespace *) namespacePtr; | | | < | 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 | */ void Tcl_DeleteNamespace( Tcl_Namespace *namespacePtr)/* Points to the namespace to delete. */ { Namespace *nsPtr = (Namespace *) namespacePtr; Tcl_Interp *interp = nsPtr->interp; Namespace *globalNsPtr = (Namespace *) TclGetGlobalNamespace(interp); Tcl_HashEntry *entryPtr; Tcl_HashSearch search; Command *cmdPtr; /* * Ensure that this namespace doesn't get deallocated in the meantime. */ |
︙ | ︙ | |||
930 931 932 933 934 935 936 | * separate list of coros. */ for (entryPtr = Tcl_FirstHashEntry(&nsPtr->cmdTable, &search); entryPtr != NULL;) { cmdPtr = (Command *)Tcl_GetHashValue(entryPtr); if (cmdPtr->nreProc == TclNRInterpCoroutine) { | | < | 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 | * separate list of coros. */ for (entryPtr = Tcl_FirstHashEntry(&nsPtr->cmdTable, &search); entryPtr != NULL;) { cmdPtr = (Command *)Tcl_GetHashValue(entryPtr); if (cmdPtr->nreProc == TclNRInterpCoroutine) { Tcl_DeleteCommandFromToken(interp, (Tcl_Command) cmdPtr); entryPtr = Tcl_FirstHashEntry(&nsPtr->cmdTable, &search); } else { entryPtr = Tcl_NextHashEntry(&search); } } /* |
︙ | ︙ | |||
954 955 956 957 958 959 960 | /* * Splice out and link to indicate that we've already been killed. */ nsPtr->ensembles = (Tcl_Ensemble *) ensemblePtr->next; ensemblePtr->next = ensemblePtr; | | | 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 | /* * Splice out and link to indicate that we've already been killed. */ nsPtr->ensembles = (Tcl_Ensemble *) ensemblePtr->next; ensemblePtr->next = ensemblePtr; Tcl_DeleteCommandFromToken(interp, ensemblePtr->token); } /* * If the namespace has a registered unknown handler (TIP 181), then free * it here. */ |
︙ | ︙ | |||
1009 1010 1011 1012 1013 1014 1015 | * being deleted, ignore any second call. */ nsPtr->flags |= (NS_DYING|NS_KILLED); TclTeardownNamespace(nsPtr); | | | 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 | * being deleted, ignore any second call. */ nsPtr->flags |= (NS_DYING|NS_KILLED); TclTeardownNamespace(nsPtr); if ((nsPtr != globalNsPtr) || (((Interp *) interp)->flags & DELETED)) { /* * If this is the global namespace, then it may have residual * "errorInfo" and "errorCode" variables for errors that occurred * while it was being torn down. Try to clear the variable list * one last time. */ |
︙ | ︙ | |||
1035 1036 1037 1038 1039 1040 1041 | nsPtr ->flags |= NS_DEAD; } else { /* * Restore the ::errorInfo and ::errorCode traces. */ | | | | 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 | nsPtr ->flags |= NS_DEAD; } else { /* * Restore the ::errorInfo and ::errorCode traces. */ EstablishErrorInfoTraces(NULL, interp, NULL, NULL, 0); EstablishErrorCodeTraces(NULL, interp, NULL, NULL, 0); /* * We didn't really kill it, so remove the KILLED marks, so it can * get killed later, avoiding mem leaks. */ nsPtr->flags &= ~(NS_DYING|NS_KILLED); |
︙ | ︙ |
Changes to generic/tclOO.c.
︙ | ︙ | |||
846 847 848 849 850 851 852 | /* * The namespace is only deleted if it hasn't already been deleted. [Bug * 2950259]. */ if (!Destructing(oPtr)) { | > > > > > > > > > > > | > > > > > > > > > > > > > | > > > > > > > > > | | > | 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 | /* * The namespace is only deleted if it hasn't already been deleted. [Bug * 2950259]. */ if (!Destructing(oPtr)) { /* * Ensure that we don't recurse very deeply and blow out the C stack. * [Bug 02977e0004] */ ThreadLocalData *tsdPtr = GetFoundation(interp)->tsdPtr; if (oPtr->classPtr) { /* * Classes currently need the recursion to get destructor calling * right. This is a bug, but it requires a major rewrite of things * to fix. */ Tcl_DeleteNamespace(oPtr->namespacePtr); oPtr->command = NULL; TclOODecrRefCount(oPtr); } else if (!tsdPtr->delQueueTail) { /* * Process a queue of objects to delete. */ Object *currPtr, *tmp; tsdPtr->delQueueTail = oPtr; for (currPtr = oPtr; currPtr; currPtr = tmp) { Tcl_DeleteNamespace(currPtr->namespacePtr); currPtr->command = NULL; tmp = currPtr->delNext; TclOODecrRefCount(currPtr); } tsdPtr->delQueueTail = NULL; } else { /* * Enqueue the object. */ tsdPtr->delQueueTail->delNext = oPtr; tsdPtr->delQueueTail = oPtr; } } else { oPtr->command = NULL; TclOODecrRefCount(oPtr); } return; } /* * ---------------------------------------------------------------------- * * TclOODeleteDescendants -- |
︙ | ︙ | |||
1098 1099 1100 1101 1102 1103 1104 | { Object *oPtr = clientData; Foundation *fPtr = oPtr->fPtr; FOREACH_HASH_DECLS; Class *mixinPtr; Method *mPtr; Tcl_Obj *filterObj, *variableObj; | | | 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 | { Object *oPtr = clientData; Foundation *fPtr = oPtr->fPtr; FOREACH_HASH_DECLS; Class *mixinPtr; Method *mPtr; Tcl_Obj *filterObj, *variableObj; Tcl_Interp *interp = fPtr->interp; int i; if (Destructing(oPtr)) { /* * TODO: Can ObjectNamespaceDeleted ever be called twice? If not, * this guard could be removed. */ |
︙ | ︙ | |||
1134 1135 1136 1137 1138 1139 1140 | * in that case when the destructor is partially deleted before the uses * of it have gone. [Bug 2949397] */ if (!Tcl_InterpDeleted(interp) && !(oPtr->flags & DESTRUCTOR_CALLED)) { CallContext *contextPtr = TclOOGetCallContext(oPtr, NULL, DESTRUCTOR, NULL); | < < < > > > | 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 | * in that case when the destructor is partially deleted before the uses * of it have gone. [Bug 2949397] */ if (!Tcl_InterpDeleted(interp) && !(oPtr->flags & DESTRUCTOR_CALLED)) { CallContext *contextPtr = TclOOGetCallContext(oPtr, NULL, DESTRUCTOR, NULL); oPtr->flags |= DESTRUCTOR_CALLED; if (contextPtr != NULL) { int result; Tcl_InterpState state; contextPtr->callPtr->flags |= DESTRUCTOR; contextPtr->skip = 0; state = Tcl_SaveInterpState(interp, TCL_OK); result = Tcl_NRCallObjProc(interp, TclOOInvokeContext, contextPtr, 0, NULL); if (result != TCL_OK) { Tcl_BackgroundException(interp, result); |
︙ | ︙ | |||
1171 1172 1173 1174 1175 1176 1177 | */ } else { /* * The namespace must have been deleted directly. Delete the command * as well. */ | | | | 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 | */ } else { /* * The namespace must have been deleted directly. Delete the command * as well. */ Tcl_DeleteCommandFromToken(interp, oPtr->command); } if (oPtr->myCommand) { Tcl_DeleteCommandFromToken(interp, oPtr->myCommand); } /* * Splice the object out of its context. After this, we must *not* call * methods on the object. */ |
︙ | ︙ | |||
1250 1251 1252 1253 1254 1255 1256 | * class of classes now as well. Due to the incestuous nature of those two * classes, if one goes the other must too and yet the tangle can * sometimes not go away automatically; we force it here. [Bug 2962664] */ if (IsRootObject(oPtr) && !Destructing(fPtr->classCls->thisPtr) && !Tcl_InterpDeleted(interp)) { | < | 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 | * class of classes now as well. Due to the incestuous nature of those two * classes, if one goes the other must too and yet the tangle can * sometimes not go away automatically; we force it here. [Bug 2962664] */ if (IsRootObject(oPtr) && !Destructing(fPtr->classCls->thisPtr) && !Tcl_InterpDeleted(interp)) { Tcl_DeleteCommandFromToken(interp, fPtr->classCls->thisPtr->command); } if (oPtr->classPtr != NULL) { TclOOReleaseClassContents(interp, oPtr); } |
︙ | ︙ |
Changes to generic/tclOOInt.h.
︙ | ︙ | |||
193 194 195 196 197 198 199 200 201 202 203 204 205 206 | Tcl_Obj *cachedNameObj; /* Cache of the name of the object. */ Tcl_HashTable *chainCache; /* Place to keep unused contexts. This table * is indexed by method name as Tcl_Obj. */ Tcl_ObjectMapMethodNameProc *mapMethodNameProc; /* Function to allow remapping of method * names. For itcl-ng. */ LIST_STATIC(Tcl_Obj *) variables; } Object; #define OBJECT_DESTRUCTING 1 /* Indicates that an object is being or has * been destroyed */ #define DESTRUCTOR_CALLED 2 /* Indicates that evaluation of destructor script for the object has began */ #define OO_UNUSED_4 4 /* No longer used. */ | > > | 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 | Tcl_Obj *cachedNameObj; /* Cache of the name of the object. */ Tcl_HashTable *chainCache; /* Place to keep unused contexts. This table * is indexed by method name as Tcl_Obj. */ Tcl_ObjectMapMethodNameProc *mapMethodNameProc; /* Function to allow remapping of method * names. For itcl-ng. */ LIST_STATIC(Tcl_Obj *) variables; struct Object *delNext; /* If non-NULL, the next object to have its * namespace deleted. */ } Object; #define OBJECT_DESTRUCTING 1 /* Indicates that an object is being or has * been destroyed */ #define DESTRUCTOR_CALLED 2 /* Indicates that evaluation of destructor script for the object has began */ #define OO_UNUSED_4 4 /* No longer used. */ |
︙ | ︙ | |||
289 290 291 292 293 294 295 296 297 298 299 300 301 302 | typedef struct ThreadLocalData { int nsCount; /* Epoch counter is used for keeping * the values used in Tcl_Obj internal * representations sane. Must be thread-local * because Tcl_Objs can cross interpreter * boundaries within a thread (objects don't * generally cross threads). */ } ThreadLocalData; typedef struct Foundation { Tcl_Interp *interp; Class *objectCls; /* The root of the object system. */ Class *classCls; /* The class of all classes. */ Tcl_Namespace *ooNs; /* ::oo namespace. */ | > > | 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 | typedef struct ThreadLocalData { int nsCount; /* Epoch counter is used for keeping * the values used in Tcl_Obj internal * representations sane. Must be thread-local * because Tcl_Objs can cross interpreter * boundaries within a thread (objects don't * generally cross threads). */ Object *delQueueTail; /* The tail object in the deletion queue. If * NULL, there is no deletion queue. */ } ThreadLocalData; typedef struct Foundation { Tcl_Interp *interp; Class *objectCls; /* The root of the object system. */ Class *classCls; /* The class of all classes. */ Tcl_Namespace *ooNs; /* ::oo namespace. */ |
︙ | ︙ |
Changes to tests/oo.test.
︙ | ︙ | |||
414 415 416 417 418 419 420 | return $::deathOrder }} 5 } -cleanup { parent destroy } -result {1 2 3 4 0} test oo-1.23 {basic test of OO functionality: deep nested ownership} -setup { oo::class create parent | | > > > > > > > > > > > > > > > > > > > > > > | 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 | return $::deathOrder }} 5 } -cleanup { parent destroy } -result {1 2 3 4 0} test oo-1.23 {basic test of OO functionality: deep nested ownership} -setup { oo::class create parent } -body { oo::class create abc { superclass parent method make {} {[self class] create xyz} destructor {incr ::count} } apply {n { set ::count 0 # Make a lot of "nested" objects set base [abc new] for {set i 1; set obj $base} {$i < $n} {incr i} { set obj [$obj make] } # Kill them all in one go; should not crash! $base destroy return [expr {$n - $::count}] }} 10000 } -cleanup { parent destroy } -result 0 test oo-1.24 {basic test of OO functionality: deep nested ownership} -setup { oo::class create parent } -constraints knownBug -body { oo::class create abc { superclass parent self method make {} {oo::copy [self] xyz} destructor {incr ::count} } apply {n { set ::count 0 # Make a lot of "nested" objects set base abc for {set i 1; set obj $base} {$i < $n} {incr i} { set obj [$obj make] } # Kill them all in one go; should not crash! $base destroy return [expr {$n - $::count}] }} 10000 |
︙ | ︙ |